﻿using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Controls.Primitives;
using System.Windows.Navigation;

using Heaven.Graphics;
using Heaven.Geometry.Procedural;
using Heaven.Geometry;
using Heaven.Mathematics;
using Heaven.Engine;
using Heaven.Engine.Animation;



using Color = Heaven.Graphics.Color;
using Colors = Heaven.Graphics.Colors;
using CameraBrowser = Heaven.Engine.CameraBrowser;
using Wavefront = Heaven.Geometry.Importers.Wavefront;
using Visual = Heaven.Engine.Visual;

namespace Heaven.Showcase.Demos
{
    /// <summary>
    /// Demonstrations. Test
    /// </summary>
	public partial class Test
    {
        #region Fields

        // Heaven device compatible with 
        // Windows Presentation Foundation
        //Heaven.Xna.Graphics.WindowsPresentationDevice device = null;
        Heaven.SlimDX.Graphics.WindowsPresentationDevice device = null;
        // Scene, as a container for objects
        Scene scene = null;
        Scene additionalScene = null;
        
        // Camera with particular behaviour
        CameraBrowser camera = new CameraBrowser();
        // It's for dragging
        double mouseX = Double.NaN;
        double mouseY = Double.NaN; 

        #endregion

        #region Initializations

        /// <summary>
        /// Constructor
        /// </summary>
        public Test()
		{
			this.InitializeComponent();
            this.Loaded += new RoutedEventHandler(OnLoaded);
            this.Unloaded += new RoutedEventHandler(OnUnloaded);
            targetImage.SizeChanged += new SizeChangedEventHandler(OnSizeChanged);
            targetImage.MouseMove += new MouseEventHandler(OnMouseMove);
            targetImage.MouseWheel += new MouseWheelEventHandler(OnMouseWheel);
  
            
            // Initialization of graphics device
            device = new Heaven.SlimDX.Graphics.WindowsPresentationDevice(targetImage, Render);
            device.DefaultColor = new Color(0, 0, 0, 0);

            // Tuning the scene
            camera.HorisontalRotation = 0.84f;
            camera.VerticalRotation = 1.07f;
            camera.Target = Vector3.Zero;
            camera.Distance = 70;

            Focusable = true;
            MouseDown += delegate { Focus(); };
            PreviewKeyDown += new KeyEventHandler(OnPreviewKeyDown);
        }

        void OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            camera.Distance *= e.Delta > 0 ? 1.05 : 0.95;
            Invalidate();
        }

        #endregion

        #region Materials construction

        /*Technique CreateMaterial()
        {
            // Creating custom node
            
            InputValueNode input = new InputValueNode(SocketTypes.FloatFour);
            OutputValueNode output = new OutputValueNode(SocketTypes.FloatThree);
            
            CustomGPUNode gpunode = new CustomGPUNode();
            Socket inputSocket = new Socket(SocketTypes.FloatFour);
            Socket outputSocket = new Socket(SocketTypes.FloatThree);
            gpunode.Inputs.Add(inputSocket);
            gpunode.Outputs.Add(outputSocket);
            gpunode.Code = "float3 f = lod2d(.....";
             
            Graph graph = new Graph();
            graph.Nodes.Add(input);
            graph.Nodes.Add(output);
            graph.Nodes.Add(gpunode);
            graph.Connectors.Add(new Connector(input.Out, inputSocket));
            graph.Connectors.Add(new Connector(output.In, outputSocket));
            
            Technique result = new Technique(device);
            result.Pipeline.Add(Graph);
            result.Update();

            return result;
             
            
        }*/

        #endregion

        #region Manipulation routines

        void OnPreviewKeyDown(object sender, KeyEventArgs e)
        {
            switch(e.Key)
            {
                case Key.D1: Test1(); break;
                case Key.D2: Test2(); break;
                case Key.D3: Test3(); break;
                case Key.D4: Test4(); break;
                case Key.D5: Test5(); break;
                case Key.F1:
                    if ((scene != null) && (additionalScene == null)) MessageBox.Show(scene.GetStatistics());
                    if ((scene != null) && (additionalScene != null)) MessageBox.Show("Scene:\n" + scene.GetStatistics() + "\nSecond:\n" + additionalScene.GetStatistics());
                    break;
            }
        }

        void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point point = e.GetPosition(this);

                if (!Double.IsNaN(mouseX))
                {
                    camera.HorisontalRotation += (float)((point.X - mouseX) / 100);
                    camera.VerticalRotation += (float)((point.Y - mouseY) / 100);
                    Invalidate();
                }                
                
                mouseX = point.X;
                mouseY = point.Y;
            }
            else
            {
                mouseX = Double.NaN;
                mouseY = Double.NaN;
            }
        }

        void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            camera.Aspect = (float)(e.NewSize.Width / e.NewSize.Height);
            Invalidate();
        }

       
        void OnUnloaded(object sender, RoutedEventArgs e)
        {
            
            device.Dispose();
        }

        void OnLoaded(object sender, RoutedEventArgs e)
        {
        }

        #endregion

        #region Tests

        Polygonal CreateSphere(double x, double y, double z, double radius)
        {
            Sphere sphere = new Sphere(new Vector3(x,y,z), radius);
            return sphere.Generate();
        }

        void Test1()
        {
            scene = new Scene(camera);
            additionalScene = null;
            
            Visual visual = new Visual(CreateSphere(0, 0, 0, 5));
            scene.Visuals.Add(visual);

            visual = new Visual(CreateSphere(0, 0, 8, 3));
            scene.Visuals.Add(visual);

            visual = new Visual(CreateSphere(0, 0, 13, 2));
            scene.Visuals.Add(visual);

            Invalidate();
        }

        void Test2()
        {
            scene = new Scene(camera);
            additionalScene = null;

            
            NodeGroup group = new NodeGroup();
            

            Visual visual1 = new Visual(CreateSphere(0, 0, 0, 5));
            group.Add(visual1);

            Visual visual2 = new Visual(CreateSphere(0, 0, 8, 3));
            

            Visual visual3 = new Visual(CreateSphere(0, 0, 13, 2));
            group.Add(visual3);

            Visual visual4 = new Visual(CreateSphere(0, 0, 15, 1));
            group.Add(visual4);

            scene.Visuals.Add(group);

            visual3.Technique = visual2.Technique;
            visual1.IsHidden = true;
            visual1.IsHidden = false;

            scene.Visuals.Remove(group);
            visual1.IsHidden = false;

            scene.Visuals.Add(group);

            group.Add(visual2);
            group.Remove(visual4);


            bool invalidatedGroup = false;
            group.Invalidated += delegate { invalidatedGroup = true; };
            bool invalidatedVisual = false;
            visual1.Invalidated += delegate { invalidatedVisual = true; };

            visual1.IsHidden = true;
            if (invalidatedVisual == false) System.Diagnostics.Debugger.Break();
            if (invalidatedGroup == false) System.Diagnostics.Debugger.Break();

            Invalidate();
        }

        void Test3()
        {
            scene = new Scene(camera);
            additionalScene = new Scene(camera);

            NodeGroup group = new NodeGroup();

            Visual visual1 = new Visual(CreateSphere(0, 0, 0, 5));
            group.Add(visual1);

            Visual visual2 = new Visual(CreateSphere(0, 0, 8, 3));
            group.Add(visual2);

            Visual visual3 = new Visual(CreateSphere(0, 0, 13, 2));
            group.Add(visual3);

            scene.Visuals.Add(group);

            additionalScene.Visuals.Add(group);
            additionalScene.Visuals.Add(visual3);
            group.Remove(visual3);

            Invalidate();
        }

        void Test4()
        {
        }

        void Test5()
        {
        }

        #endregion
               
        #region Rendering

      
        void Invalidate()
        {
            device.Invalidate();
        }

        void Render()
        {
            device.Begin();
            try
            {
                device.Clear();
                if(scene != null) scene.Render(device);
                if (additionalScene != null) additionalScene.Render(device);
            }
            finally
            {
                device.Present();
            }
        }

        #endregion
    }
}